Skip to content

feat(sheet): 37 missing Excel functions, array spill rendering, _xlfn round trip - #373

Merged
SamTV12345 merged 1 commit into
mainfrom
feat/excel-functions-batch1
Jul 28, 2026
Merged

feat(sheet): 37 missing Excel functions, array spill rendering, _xlfn round trip#373
SamTV12345 merged 1 commit into
mainfrom
feat/excel-functions-batch1

Conversation

@SamTV12345

Copy link
Copy Markdown
Member

Was fehlt(e)

HyperFormula bringt 418 Funktionen mit, aber praktisch keine der modernen Excel-Funktionen. Dieser PR schließt die größten Lücken.

Neue Funktionen (als ein HyperFormula-Plugin, damit sie sich wie Built-ins verhalten)

  • Text: CONCAT, TEXTBEFORE, TEXTAFTER, NUMBERVALUE, FIXED, DOLLAR
  • Lookup: XMATCH, LOOKUP
  • Dynamic Arrays: UNIQUE, SORT, SORTBY, TAKE, DROP, VSTACK, HSTACK, TOCOL, TOROW, CHOOSECOLS, CHOOSEROWS, EXPAND
  • Statistik: AVERAGEIFS, RANK / RANK.EQ / RANK.AVG, MODE / MODE.SNGL / MODE.MULT, TRIMMEAN, PERMUT, PERMUTATIONA, INTERCEPT, FORECAST / FORECAST.LINEAR, FREQUENCY
  • Info: ERROR.TYPE, TYPE
  • Finanzen: XIRR

Autocomplete in der Formelleiste zieht die Namen automatisch aus der HyperFormula-Registry, also ohne weitere Verdrahtung.

Array-Spill wird gerendert

Bisher zeigte nur die Formelzelle selbst einen Wert — Zellen ohne eigenen raw blieben leer, wodurch jedes Array-Ergebnis (auch das schon vorhandene FILTER/SEQUENCE/TRANSPOSE) unsichtbar war. Leere Zellen fragen jetzt die Engine, spilled Ranges erscheinen wie in Excel.

XLSX-Round-Trip

Excel speichert alles ab 2010 mit Namespace (_xlfn.XLOOKUP, _xlfn._xlws.SORT, _xlfn.NORM.DIST). Import strippt die Präfixe, Export setzt sie wieder — vorher zeigte Excel #NAME? für jede moderne Formel, die wir geschrieben haben (betraf auch schon TEXTJOIN/IFS/XLOOKUP).

Tests

26 neue Vitest-Cases (excelFunctions.test.ts) gegen ein Fixture-Grid, plus Go-Tests für die Präfix-Umschreibung inkl. String-Literalen. go test ./lib/xlsx/... ./lib/sheetdoc/... und vitest run (119 Tests) grün.

Bewusst ausgelassen

  • LET / LAMBDA und INDIRECT (brauchen Parser- bzw. Dependency-Graph-Support in HyperFormula)
  • TEXTSPLIT (Spill-Größe lässt sich nicht vorhersagen; SPLIT deckt den Fall ab)
  • Bond-Familie (PRICE, YIELD, DURATION, ACCRINT …) — braucht Day-Count-Basis-Logik, eigener Batch

🤖 Generated with Claude Code

HyperFormula ships 418 functions but misses most of the modern Excel set.
Register them as one plugin so they behave like built-ins (coercion, errors,
autocomplete): CONCAT, TEXTBEFORE/TEXTAFTER, NUMBERVALUE, FIXED, DOLLAR,
XMATCH, LOOKUP, UNIQUE, SORT, SORTBY, TAKE, DROP, VSTACK, HSTACK, TOCOL,
TOROW, CHOOSECOLS, CHOOSEROWS, EXPAND, AVERAGEIFS, RANK(.EQ/.AVG),
MODE(.SNGL/.MULT), TRIMMEAN, PERMUT, PERMUTATIONA, INTERCEPT,
FORECAST(.LINEAR), FREQUENCY, ERROR.TYPE, TYPE, XIRR.

Array results now render: blank cells fall back to the engine value, so
spilled ranges (UNIQUE, SORT, SEQUENCE, FILTER) are visible like in Excel.

XLSX round trip: Excel namespaces post-2007 functions (_xlfn.XLOOKUP,
_xlfn._xlws.SORT). Strip on import, add back on export - without it every
modern formula we wrote showed #NAME? in Excel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 28, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add 37 Excel functions, spill rendering, and XLSX namespaces

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Adds 37 Excel-compatible functions through a globally registered HyperFormula plugin.
• Renders dynamic-array spill values and errors in otherwise empty grid cells.
• Preserves modern Excel function namespaces across XLSX import and export.
Diagram

graph TD
  X["XLSX File"] -->|reads| I["XLSX Import"] -->|strips prefixes| N["Namespace Mapper"] -->|stores formulas| W["Workbook Model"] -->|loads cells| E["Formula Engine"] -->|registers| P["Excel Extras"]
  E -->|computed spills| S["Sheet Editor"]
  W -->|exports formulas| N -->|adds prefixes| O["XLSX Export"] -->|writes| X
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Adopt a broader Excel-compatible engine
  • ➕ Reduces custom implementations of Excel semantics and numerical functions.
  • ➕ Could provide wider modern-function coverage without additional plugins.
  • ➖ Requires a high-risk replacement of the existing HyperFormula integration.
  • ➖ May regress browser support, dependency tracking, licensing, or existing formulas.
  • ➖ Still requires validating XLSX namespaces and UI spill rendering.
2. Split plugins by function family
  • ➕ Improves ownership and test isolation for text, lookup, array, and statistical functions.
  • ➕ Keeps individual modules smaller as function coverage expands.
  • ➖ Adds registration and translation wiring for several plugins.
  • ➖ Shared coercion, comparison, and array helpers need another common abstraction.
  • ➖ Provides no immediate behavioral advantage over one registered plugin.

Recommendation: Extending the established HyperFormula wrapper is preferable to replacing the calculation engine and keeps autocomplete, coercion, errors, and dependency tracking integrated. The single-plugin approach is reasonable for this batch, although family-specific modules should be considered if function coverage continues growing.

Files changed (8) +1424 / -4

Enhancement (3) +1154 / -0
formulanames.goAdd Excel function namespace translation +110/-0

Add Excel function namespace translation

• Introduces import stripping and export prefixing for post-2007 functions, including worksheet-scoped dynamic-array names. The scanner recognizes function calls case-insensitively while preserving quoted string literals.

lib/xlsx/formulanames.go

excelFunctions.tsImplement 37 missing Excel functions +1042/-0

Implement 37 missing Excel functions

• Adds a HyperFormula plugin covering modern text, lookup, dynamic-array, statistical, information, and financial functions. It includes shared Excel-style coercion and comparison helpers, aliases, error handling, array-size predictions, and idempotent global registration.

ui/src/js/sheet/excelFunctions.ts

formulaEngine.tsRegister Excel extras in each formula engine +2/-0

Register Excel extras in each formula engine

• Registers the additional function plugin before creating a HyperFormula instance. The idempotent registration makes the functions available to evaluation and autocomplete.

ui/src/js/sheet/formulaEngine.ts

Bug fix (3) +11 / -4
export.goNamespace modern formulas during XLSX export +1/-1

Namespace modern formulas during XLSX export

• Passes formulas through the new prefix mapper before writing them with excelize. This prevents modern formulas exported by the application from appearing as unknown names in Excel.

lib/xlsx/export.go

import.goNormalize namespaced formulas during XLSX import +1/-1

Normalize namespaced formulas during XLSX import

• Removes Excel's '_xlfn' and '_xlws' prefixes before storing imported formulas. This allows the local formula engine to resolve modern function names.

lib/xlsx/import.go

sheetEditor.tsRender dynamic-array spill cells and errors +9/-2

Render dynamic-array spill cells and errors

• Falls back to calculated engine values when a grid cell has no raw content, making spilled arrays visible. Error lookup now also examines blank cells that may receive spilled errors.

ui/src/js/sheet/sheetEditor.ts

Tests (2) +259 / -0
formulanames_test.goTest XLSX function namespace round trips +37/-0

Test XLSX function namespace round trips

• Covers standard, nested, dotted, and worksheet-scoped function names. It also verifies that string literals and sheet-qualified references remain unchanged.

lib/xlsx/formulanames_test.go

excelFunctions.test.tsValidate added Excel functions and dynamic arrays +222/-0

Validate added Excel functions and dynamic arrays

• Adds fixture-based Vitest coverage for function registration, text, lookup, dynamic-array, statistical, information, and financial behavior. Tests include spill results and representative Excel error cases.

ui/src/js/sheet/excelFunctions.test.ts

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add 37 Excel functions, spill rendering, and XLSX namespaces

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Adds 37 Excel-compatible functions through a registered HyperFormula plugin.
• Renders dynamic-array spill values and errors in otherwise empty cells.
• Preserves modern Excel formulas across XLSX import/export using namespace normalization.
Diagram

graph TD
  X["XLSX File"] --> I["XLSX Import"] --> F["Plain Formula"] --> E["Formula Engine"] --> H["HyperFormula"] --> S["Spill Renderer"]
  E --> P["Excel Extras"] --> H
  F --> O["XLSX Export"] --> X
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Split plugins by function family
  • ➕ Reduces individual module size
  • ➕ Allows focused ownership and testing by domain
  • ➕ Makes future function batches easier to isolate
  • ➖ Requires coordinating multiple global plugin registrations
  • ➖ May duplicate shared coercion, range, and error helpers
  • ➖ Adds structure without changing current user behavior
2. Maintain a HyperFormula fork
  • ➕ Could expose stronger internal types and native implementations
  • ➕ Centralizes Excel compatibility inside the calculation dependency
  • ➖ Creates a costly long-lived fork
  • ➖ Complicates dependency upgrades
  • ➖ Still requires application-side spill rendering and XLSX normalization
3. Implement functions outside HyperFormula
  • ➕ Avoids reliance on non-public plugin interpreter types
  • ➕ Provides complete control over evaluation
  • ➖ Duplicates parsing, coercion, errors, dependency tracking, and array behavior
  • ➖ Functions would not automatically participate in the registry or autocomplete
  • ➖ Creates inconsistent behavior between built-in and added functions

Recommendation: The registered HyperFormula plugin is the best approach because it reuses existing parsing, coercion, errors, dependency tracking, spilling, and autocomplete. Keeping one plugin is reasonable for this batch due to shared helpers; splitting by function family should only be considered as subsequent batches expand the module.

Files changed (8) +1424 / -4

Enhancement (3) +1154 / -0
formulanames.goNormalize modern Excel function namespaces +110/-0

Normalize modern Excel function namespaces

• Adds rules for identifying post-2007 and worksheet-scoped functions. Implements quote-aware export rewriting and import stripping without modifying function-like text inside string literals.

lib/xlsx/formulanames.go

excelFunctions.tsImplement 37 missing Excel functions +1042/-0

Implement 37 missing Excel functions

• Introduces a HyperFormula plugin covering text, lookup, dynamic-array, statistical, information, and financial functions with Excel-style coercion and errors. Adds aliases, shared range helpers, spill-size predictions, numerical solvers, and idempotent global registration.

ui/src/js/sheet/excelFunctions.ts

formulaEngine.tsRegister Excel extensions when creating formula engines +2/-0

Register Excel extensions when creating formula engines

• Registers the additional function plugin before constructing HyperFormula, making the functions available to evaluation and registry-driven autocomplete.

ui/src/js/sheet/formulaEngine.ts

Bug fix (3) +11 / -4
export.goRestore Excel namespaces during formula export +1/-1

Restore Excel namespaces during formula export

• Routes exported formulas through the namespace rewriter before writing them to XLSX. This prevents modern functions from reopening as #NAME? in Excel.

lib/xlsx/export.go

import.goStrip Excel namespaces during formula import +1/-1

Strip Excel namespaces during formula import

• Converts namespaced XLSX formulas into plain function names understood by the internal formula engine.

lib/xlsx/import.go

sheetEditor.tsRender values and errors from spilled arrays +9/-2

Render values and errors from spilled arrays

• Falls back to calculated engine values when cells have no raw content. It also exposes errors spilled into otherwise blank cells.

ui/src/js/sheet/sheetEditor.ts

Tests (2) +259 / -0
formulanames_test.goTest formula namespace rewriting and round trips +37/-0

Test formula namespace rewriting and round trips

• Covers standard, modern, worksheet-scoped, nested, and dotted function names. Verifies string literals and sheet-qualified references remain unchanged.

lib/xlsx/formulanames_test.go

excelFunctions.test.tsValidate added Excel functions and dynamic arrays +222/-0

Validate added Excel functions and dynamic arrays

• Adds fixture-driven Vitest coverage for registration, text, lookup, dynamic-array, statistical, information, and financial functions. Tests successful calculations, spill cells, aliases, and representative Excel errors.

ui/src/js/sheet/excelFunctions.test.ts

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (7) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. CSV omits visible spills 🐞 Bug ≡ Correctness
Description
Spilled values are now rendered in cells without persisted raw content, but CSV bounds are still
calculated only from nonempty raw cells. A sheet with =SEQUENCE(10) only in A1 visibly has ten
rows but exports just the anchor row.
Code

ui/src/js/sheet/sheetEditor.ts[R181-182]

+      const spilled = engine.getValue(r, c);
+      return spilled.type === 'empty' ? '' : formatValue(spilled.value, '', propsOf(r, c).numFmt);
Relevance

⭐⭐⭐ High

PR 361 explicitly requires CSV export of computed values users see across the used range.

PR-#361

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
displayValue now asks the engine for every blank cell and renders nonempty spill results. The CSV
path nevertheless derives maxRow and maxCol solely from cellsOfActive() entries whose raw
value is nonempty before serializing that limited range.

ui/src/js/sheet/sheetEditor.ts[175-185]
ui/src/js/sheet/sheetEditor.ts[320-332]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CSV export omits visible rows and columns produced by array spills because its used-range calculation only considers persisted raw cells.

## Issue Context
Extend CSV bounds using nonempty evaluated spill cells, or expose spill ranges from the formula engine. Avoid an unbounded worksheet scan and add a dynamic-array CSV regression test.

## Fix Focus Areas
- ui/src/js/sheet/sheetEditor.ts[175-185]
- ui/src/js/sheet/sheetEditor.ts[320-332]
- ui/src/js/sheet/formulaEngine.ts[43-47]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. XIRR pairs shift silently 🐞 Bug ≡ Correctness
Description
xirr filters cash flows and dates independently before pairing them by index. Nonnumeric entries
at different positions can therefore associate a cash flow with the wrong date while still passing
the equal-length check, producing an incorrect financial result.
Code

ui/src/js/sheet/excelFunctions.ts[R642-644]

+        const cash = nums(vs);
+        const when = nums(ds);
+        if (cash.length !== when.length || cash.length < 2) return numErr('XIRR needs matching values and dates.');
Relevance

⭐⭐ Medium

Positional correctness bugs are accepted, but no XIRR or paired-range precedent exists.

PR-#352

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The implementation creates cash and when through separate nums calls and subsequently uses
when[i] for cash[i]. The existing regression helper demonstrates the safe positional approach by
retaining an entry only when both values at the same original index are numeric.

ui/src/js/sheet/excelFunctions.ts[638-648]
ui/src/js/sheet/excelFunctions.ts[132-150]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`XIRR` independently compacts its values and dates, which can shift positional associations and calculate a rate from incorrect pairs.

## Issue Context
Validate original range lengths and process entries positionally. Reject invalid dates or filter whole pairs according to the intended Excel semantics, rather than filtering each range independently.

## Fix Focus Areas
- ui/src/js/sheet/excelFunctions.ts[632-668]
- ui/src/js/sheet/excelFunctions.ts[132-150]
- ui/src/js/sheet/excelFunctions.test.ts[207-221]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Prefix stripping rewrites literals 🐞 Bug ≡ Correctness
Description
stripFunctionPrefixes removes namespace substrings from the entire imported formula instead of
only function tokens. For example, the formula ="_xlfn.XLOOKUP(" is silently changed to
="XLOOKUP(", corrupting its string-literal content.
Code

lib/xlsx/formulanames.go[R48-50]

+	for _, p := range []string{"_xlfn._xlws.", "_xlfn.", "_xlws."} {
+		formula = strings.ReplaceAll(formula, p, "")
+	}
Relevance

⭐⭐ Medium

No historical evidence addresses preserving formula literals during XLSX namespace stripping.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Import passes the complete formula through stripFunctionPrefixes, which uses unconditional
ReplaceAll. In contrast, the export scanner explicitly skips quoted strings, proving that formula
literals require lexical handling.

lib/xlsx/import.go[58-61]
lib/xlsx/formulanames.go[45-51]
lib/xlsx/formulanames.go[54-80]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
XLSX import strips namespace substrings inside quoted formula text and other non-function tokens.

## Issue Context
Use a quote-aware and token-aware scanner similar to the export implementation. Remove a prefix only when it directly qualifies a function call, and add tests for quoted literals and escaped quotes.

## Fix Focus Areas
- lib/xlsx/formulanames.go[45-51]
- lib/xlsx/formulanames.go[54-101]
- lib/xlsx/formulanames_test.go[27-37]
- lib/xlsx/import.go[58-61]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Legacy function gets prefixed 🐞 Bug ≡ Correctness
Description
needsPrefix classifies every dotted name as a post-2007 function, so the newly supported legacy
ERROR.TYPE exports as _xlfn.ERROR.TYPE. That namespace form is not valid for ERROR.TYPE and
can make the exported formula unrecognized.
Code

lib/xlsx/formulanames.go[R40-42]

+	// Dotted names are the 2010+ statistical/compatibility set (NORM.DIST,
+	// MODE.SNGL, CEILING.MATH, ...), all of which Excel namespaces.
+	return strings.Contains(name, ".")
Relevance

⭐⭐ Medium

No historical evidence addresses namespace handling for legacy dotted Excel functions.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The export path applies addFunctionPrefixes to every formula, and its dotted-name rule necessarily
matches ERROR.TYPE. The new plugin explicitly registers and tests that dotted function name.

lib/xlsx/export.go[62-64]
lib/xlsx/formulanames.go[36-42]
ui/src/js/sheet/excelFunctions.ts[597-616]
ui/src/js/sheet/excelFunctions.ts[1013-1016]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The blanket dotted-name heuristic incorrectly namespaces legacy dotted functions such as `ERROR.TYPE` during XLSX export.

## Issue Context
Replace the heuristic with an explicit classification of functions requiring `_xlfn` or add a verified legacy exclusion set. Add an export regression test for `ERROR.TYPE`.

## Fix Focus Areas
- lib/xlsx/formulanames.go[17-43]
- lib/xlsx/formulanames_test.go[5-24]
- ui/src/js/sheet/excelFunctions.ts[597-616]
- ui/src/js/sheet/excelFunctions.ts[1013-1016]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. FIXED rounds negative ties wrong 🐞 Bug ≡ Correctness
Description
fixedText uses Math.round for negative decimal places, which rounds negative half-ties toward
positive infinity rather than away from zero. Consequently, FIXED(-125,-1) returns -120 instead
of Excel-compatible -130.
Code

ui/src/js/sheet/excelFunctions.ts[R103-106]

+const fixedText = (n: number, decimals: number, commas: boolean): string => {
+  const d = Math.trunc(decimals);
+  const rounded = d < 0 ? Math.round(n / 10 ** -d) * 10 ** -d : n;
+  const s = rounded.toFixed(Math.max(0, d));
Relevance

⭐⭐ Medium

Excel-parity regressions were fixed, but no history covers negative tie rounding.

PR-#364

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The helper claims Excel-compatible left-of-decimal rounding but applies JavaScript Math.round
directly to a signed quotient. FIXED passes negative inputs through that path without first
normalizing the sign.

ui/src/js/sheet/excelFunctions.ts[101-108]
ui/src/js/sheet/excelFunctions.ts[256-259]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`FIXED` produces incorrect results for negative numbers at half-ties when rounding left of the decimal point.

## Issue Context
Implement explicit half-away-from-zero rounding, such as rounding the absolute magnitude and restoring the sign. Add regression coverage for positive and negative tie cases.

## Fix Focus Areas
- ui/src/js/sheet/excelFunctions.ts[101-108]
- ui/src/js/sheet/excelFunctions.ts[256-266]
- ui/src/js/sheet/excelFunctions.test.ts[54-60]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

6. Computed EXPAND sizes truncate 🐞 Bug ≡ Correctness
Description
expandSize reserves only the input dimensions when row or column counts come from cell references
or expressions, although expand computes the larger matrix. Formulas such as =EXPAND(A1,B1,C1)
therefore spill only part of their result.
Code

ui/src/js/sheet/excelFunctions.ts[R735-737]

+    const literal = (i: number): number | undefined =>
+      ast.args[i] !== undefined && ast.args[i].type === 'NUMBER' ? ast.args[i].value : undefined;
+    return new ArraySize(literal(2) ?? sizes[0].width, literal(1) ?? sizes[0].height);
Relevance

⭐ Low

PR 328 rejected analogous fixed-grid truncation feedback; code explicitly treats computed EXPAND
sizes as unsupported.

PR-#328

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The evaluator accepts arbitrary computed r and c values and constructs the requested output,
while the size predictor explicitly falls back to the source range for every non-literal argument.
The file also states that an undersized hint truncates an array result.

ui/src/js/sheet/excelFunctions.ts[467-476]
ui/src/js/sheet/excelFunctions.ts[673-677]
ui/src/js/sheet/excelFunctions.ts[729-738]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`EXPAND` returns a matrix based on evaluated row and column arguments, but its spill-size method recognizes only numeric literals. Computed dimensions consequently produce truncated spills.

## Issue Context
Evaluate scalar dimension arguments during size calculation where supported, or reserve a safe validated upper bound. Add tests using cell references and expressions for both dimensions.

## Fix Focus Areas
- ui/src/js/sheet/excelFunctions.ts[467-476]
- ui/src/js/sheet/excelFunctions.ts[729-738]
- ui/src/js/sheet/excelFunctions.test.ts[128-131]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Blank cells trigger lookup storm 🐞 Bug ➹ Performance
Description
Each render now invokes engine.getValue through both displayValue and errorOf for virtually
every blank cell. On the fixed 200×52 grid this adds roughly 20,800 avoidable HyperFormula API
lookups per render, including renders caused by selection and presence updates.
Code

ui/src/js/sheet/sheetEditor.ts[R494-496]

+        // '' included: an array formula can spill an error into a blank cell.
+        if (cell && cell.raw !== '' && !cell.raw.startsWith('=')) return undefined;
        const res = engine.getValue(r, c);
Relevance

⭐ Low

PR 318 rejected analogous render-loop performance optimization despite frequent repeated work.

PR-#318

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The editor creates a 10,400-cell grid. DomSheetView.render calls both callbacks for every
coordinate; both callbacks now query HyperFormula for absent or empty cells, doubling the avoidable
lookup count.

ui/src/js/sheet/sheetEditor.ts[64-65]
ui/src/js/sheet/sheetEditor.ts[175-182]
ui/src/js/sheet/sheetEditor.ts[489-498]
ui/src/js/sheet/sheetView.ts[431-468]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Spill rendering causes two formula-engine lookups for every blank grid coordinate on every full render.

## Issue Context
Share one evaluated result between display and error rendering, batch-fetch engine values, or maintain known spill ranges so ordinary blank cells retain the fast path. Preserve spilled error rendering.

## Fix Focus Areas
- ui/src/js/sheet/sheetEditor.ts[64-65]
- ui/src/js/sheet/sheetEditor.ts[175-185]
- ui/src/js/sheet/sheetEditor.ts[489-498]
- ui/src/js/sheet/sheetView.ts[431-468]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +642 to +644
const cash = nums(vs);
const when = nums(ds);
if (cash.length !== when.length || cash.length < 2) return numErr('XIRR needs matching values and dates.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Xirr pairs shift silently 🐞 Bug ≡ Correctness

xirr filters cash flows and dates independently before pairing them by index. Nonnumeric entries
at different positions can therefore associate a cash flow with the wrong date while still passing
the equal-length check, producing an incorrect financial result.
Agent Prompt
## Issue description
`XIRR` independently compacts its values and dates, which can shift positional associations and calculate a rate from incorrect pairs.

## Issue Context
Validate original range lengths and process entries positionally. Reject invalid dates or filter whole pairs according to the intended Excel semantics, rather than filtering each range independently.

## Fix Focus Areas
- ui/src/js/sheet/excelFunctions.ts[632-668]
- ui/src/js/sheet/excelFunctions.ts[132-150]
- ui/src/js/sheet/excelFunctions.test.ts[207-221]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +181 to +182
const spilled = engine.getValue(r, c);
return spilled.type === 'empty' ? '' : formatValue(spilled.value, '', propsOf(r, c).numFmt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Csv omits visible spills 🐞 Bug ≡ Correctness

Spilled values are now rendered in cells without persisted raw content, but CSV bounds are still
calculated only from nonempty raw cells. A sheet with =SEQUENCE(10) only in A1 visibly has ten
rows but exports just the anchor row.
Agent Prompt
## Issue description
CSV export omits visible rows and columns produced by array spills because its used-range calculation only considers persisted raw cells.

## Issue Context
Extend CSV bounds using nonempty evaluated spill cells, or expose spill ranges from the formula engine. Avoid an unbounded worksheet scan and add a dynamic-array CSV regression test.

## Fix Focus Areas
- ui/src/js/sheet/sheetEditor.ts[175-185]
- ui/src/js/sheet/sheetEditor.ts[320-332]
- ui/src/js/sheet/formulaEngine.ts[43-47]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread lib/xlsx/formulanames.go
Comment on lines +48 to +50
for _, p := range []string{"_xlfn._xlws.", "_xlfn.", "_xlws."} {
formula = strings.ReplaceAll(formula, p, "")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Prefix stripping rewrites literals 🐞 Bug ≡ Correctness

stripFunctionPrefixes removes namespace substrings from the entire imported formula instead of
only function tokens. For example, the formula ="_xlfn.XLOOKUP(" is silently changed to
="XLOOKUP(", corrupting its string-literal content.
Agent Prompt
## Issue description
XLSX import strips namespace substrings inside quoted formula text and other non-function tokens.

## Issue Context
Use a quote-aware and token-aware scanner similar to the export implementation. Remove a prefix only when it directly qualifies a function call, and add tests for quoted literals and escaped quotes.

## Fix Focus Areas
- lib/xlsx/formulanames.go[45-51]
- lib/xlsx/formulanames.go[54-101]
- lib/xlsx/formulanames_test.go[27-37]
- lib/xlsx/import.go[58-61]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread lib/xlsx/formulanames.go
Comment on lines +40 to +42
// Dotted names are the 2010+ statistical/compatibility set (NORM.DIST,
// MODE.SNGL, CEILING.MATH, ...), all of which Excel namespaces.
return strings.Contains(name, ".")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

5. Legacy function gets prefixed 🐞 Bug ≡ Correctness

needsPrefix classifies every dotted name as a post-2007 function, so the newly supported legacy
ERROR.TYPE exports as _xlfn.ERROR.TYPE. That namespace form is not valid for ERROR.TYPE and
can make the exported formula unrecognized.
Agent Prompt
## Issue description
The blanket dotted-name heuristic incorrectly namespaces legacy dotted functions such as `ERROR.TYPE` during XLSX export.

## Issue Context
Replace the heuristic with an explicit classification of functions requiring `_xlfn` or add a verified legacy exclusion set. Add an export regression test for `ERROR.TYPE`.

## Fix Focus Areas
- lib/xlsx/formulanames.go[17-43]
- lib/xlsx/formulanames_test.go[5-24]
- ui/src/js/sheet/excelFunctions.ts[597-616]
- ui/src/js/sheet/excelFunctions.ts[1013-1016]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +103 to +106
const fixedText = (n: number, decimals: number, commas: boolean): string => {
const d = Math.trunc(decimals);
const rounded = d < 0 ? Math.round(n / 10 ** -d) * 10 ** -d : n;
const s = rounded.toFixed(Math.max(0, d));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

7. Fixed rounds negative ties wrong 🐞 Bug ≡ Correctness

fixedText uses Math.round for negative decimal places, which rounds negative half-ties toward
positive infinity rather than away from zero. Consequently, FIXED(-125,-1) returns -120 instead
of Excel-compatible -130.
Agent Prompt
## Issue description
`FIXED` produces incorrect results for negative numbers at half-ties when rounding left of the decimal point.

## Issue Context
Implement explicit half-away-from-zero rounding, such as rounding the absolute magnitude and restoring the sign. Add regression coverage for positive and negative tie cases.

## Fix Focus Areas
- ui/src/js/sheet/excelFunctions.ts[101-108]
- ui/src/js/sheet/excelFunctions.ts[256-266]
- ui/src/js/sheet/excelFunctions.test.ts[54-60]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@SamTV12345
SamTV12345 merged commit 7ef713b into main Jul 28, 2026
13 checks passed
@SamTV12345
SamTV12345 deleted the feat/excel-functions-batch1 branch July 28, 2026 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant